﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO.Pipelines;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Atomic
{
    //public class Server : IServer
    //{
    //    //private Socket _server;
    //    //public Server(ILoggerFactory loggerFactory)
    //    //{

    //    //}
    //    ////private void InitServer()
    //    ////{
    //    ////    var IPEndPoint = new IPEndPoint(IPAddress.Any, _configuration.TcpPort);
    //    ////    _server = new Socket(IPEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    //    ////    _server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
    //    ////    _server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
    //    ////    _server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, _configuration.MiniNumBufferSize);
    //    ////    _server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendBuffer, _configuration.MiniNumBufferSize);
    //    ////    _server.LingerState = new LingerOption(false, 0);
    //    ////    _server.Bind(IPEndPoint);
    //    ////    _server.Listen(_configuration.SoBacklog);
    //    ////    // 设置 TCP 心跳，空闲 15 秒，每 5 秒检查一次
    //    ////    byte[] inOptionValues = new byte[4 * 3];
    //    ////    BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
    //    ////    BitConverter.GetBytes((uint)15000).CopyTo(inOptionValues, 4);
    //    ////    BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, 8);
    //    ////    _server.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    //    ////}

    //    //public Task StartAsync(CancellationToken cancellationToken)
    //    //{
    //    //    return Task.CompletedTask;
    //    //}

    //    //// Graceful shutdown if possible
    //    //public Task StopAsync(CancellationToken cancellationToken)
    //    //{
    //    //    return Task.CompletedTask;
    //    //}

    //    //// Ungraceful shutdown
    //    //public void Dispose()
    //    //{

    //    //}

    //    private KestrelServerImpl _innerKestrelServer;

    //    public Server(IOptions<KestrelServerOptions> options, IConnectionListenerFactory transportFactory, ILoggerFactory loggerFactory)
    //    {
    //        _innerKestrelServer = new KestrelServerImpl(
    //            options,
    //            new[] { transportFactory ?? throw new ArgumentNullException(nameof(transportFactory)) },
    //            loggerFactory);
    //    }

    //    public IFeatureCollection Features => _innerKestrelServer.Features;

    //    public KestrelServerOptions Options => _innerKestrelServer.Options;

    //    public Task StartAsync(CancellationToken cancellationToken)
    //    {
    //        return _innerKestrelServer.StartAsync(cancellationToken);
    //    }

    //    // Graceful shutdown if possible
    //    public Task StopAsync(CancellationToken cancellationToken)
    //    {
    //        return _innerKestrelServer.StopAsync(cancellationToken);
    //    }

    //    // Ungraceful shutdown
    //    public void Dispose()
    //    {
    //        _innerKestrelServer.Dispose();
    //    }
    //}
    public class KestrelServerImpl : IServer
    {
 
        private readonly TransportManager _transportManager;
        private readonly IConnectionListenerFactory _transportFactory;
        //private readonly IMultiplexedConnectionListenerFactory _multiplexedTransportFactory;

        private readonly SemaphoreSlim _bindSemaphore = new SemaphoreSlim(initialCount: 1);
        private bool _hasStarted;
        private int _stopping;
        private readonly CancellationTokenSource _stopCts = new CancellationTokenSource();
        private readonly TaskCompletionSource _stoppedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);

        private IDisposable _configChangedRegistration;

        public KestrelServerImpl(IServiceProvider serviceProvider,
            //IOptions<KestrelServerOptions> options,
            IEnumerable<IConnectionListenerFactory> transportFactories,
            ILoggerFactory loggerFactory)
            : this(transportFactories, CreateServiceContext(serviceProvider, loggerFactory))
        {
        }

        // For testing
        internal KestrelServerImpl(IConnectionListenerFactory transportFactory, ServiceContext serviceContext)
            : this(new[] { transportFactory }, serviceContext)
        {
        }

        // For testing
        internal KestrelServerImpl(
            IEnumerable<IConnectionListenerFactory> transportFactories,
            ServiceContext serviceContext)
        {
            if (transportFactories == null)
            {
                throw new ArgumentNullException(nameof(transportFactories));
            }

            _transportFactory = transportFactories?.LastOrDefault();

            if (_transportFactory == null)
            {
                throw new InvalidOperationException("IConnectionListenerFactory Not Found.");
            }

            ServiceContext = serviceContext;

 
            _transportManager = new TransportManager(_transportFactory, ServiceContext);

            //HttpCharacters.Initialize();
        }

        private static ServiceContext CreateServiceContext(IServiceProvider serviceProvider, /*IOptions<KestrelServerOptions> options, */ILoggerFactory loggerFactory)
        {
            //if (options == null)
            //{
            //    throw new ArgumentNullException(nameof(options));
            //}
            if (loggerFactory == null)
            {
                throw new ArgumentNullException(nameof(loggerFactory));
            }

            var serverOptions = /*options.Value ??*/ new KestrelServerOptions();
            serverOptions.ApplicationServices = serviceProvider;
            var logger = loggerFactory.CreateLogger("Microsoft.AspNetCore.Server.Kestrel");
            var trace = new KestrelTrace(logger);
            var connectionManager = new ConnectionManager(
                trace,
                serverOptions.Limits.MaxConcurrentUpgradedConnections);

            var heartbeatManager = new HeartbeatManager(connectionManager);
            //var dateHeaderValueManager = new DateHeaderValueManager();

            var heartbeat = new Heartbeat(
                new IHeartbeatHandler[] { heartbeatManager },
                new SystemClock(),
                DebuggerWrapper.Singleton,
                trace);

            return new ServiceContext
            {
                Log = trace,

                Scheduler = PipeScheduler.ThreadPool,
                SystemClock = heartbeatManager,

                ConnectionManager = connectionManager,
                Heartbeat = heartbeat,
                ServerOptions = serverOptions,
            };
        }

        public IFeatureCollection Features { get; }

        public KestrelServerOptions Options => ServiceContext.ServerOptions;

        private ServiceContext ServiceContext { get; }

        private IKestrelTrace Trace => ServiceContext.Log;

 
        public async Task StartAsync(ConnectionDelegate connectionDelegate,CancellationToken cancellationToken)
        {
            try
            {
                if (!BitConverter.IsLittleEndian)
                {
                    throw new PlatformNotSupportedException("Big Endian Not Supported.");
                }
 
                if (_hasStarted)
                {
                    // The server has already started and/or has not been cleaned up yet
                    throw new InvalidOperationException("Server Already Started");
                }
                _hasStarted = true;

                ServiceContext.Heartbeat?.Start();

                var IPEndPoint = new IPEndPoint(IPAddress.Any, 808);
                Options.Limits.MaxConcurrentConnections = 5;

                // Add the connection limit middleware
                connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace);

                await _transportManager.BindAsync(IPEndPoint, connectionDelegate, null).ConfigureAwait(false);
 
            }
            catch (Exception ex)
            {
                Trace.LogCritical(0, ex, "Unable to start Kestrel.");
                Dispose();
                throw;
            }
        }


        // Graceful shutdown if possible
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (Interlocked.Exchange(ref _stopping, 1) == 1)
            {
                await _stoppedTcs.Task.ConfigureAwait(false);
                return;
            }

            _stopCts.Cancel();

            // Don't use cancellationToken when acquiring the semaphore. Dispose calls this with a pre-canceled token.
            await _bindSemaphore.WaitAsync().ConfigureAwait(false);

            try
            {
                await _transportManager.StopAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                _stoppedTcs.TrySetException(ex);
                throw;
            }
            finally
            {
                ServiceContext.Heartbeat?.Dispose();
                _configChangedRegistration?.Dispose();
                _stopCts.Dispose();
                _bindSemaphore.Release();
            }

            _stoppedTcs.TrySetResult();
        }

        // Ungraceful shutdown
        public void Dispose()
        {
            StopAsync(new CancellationToken(canceled: true)).GetAwaiter().GetResult();
        }


        private static ConnectionDelegate EnforceConnectionLimit(ConnectionDelegate innerDelegate, long? connectionLimit, IKestrelTrace trace)
        {
            if (!connectionLimit.HasValue)
            {
                return innerDelegate;
            }

            return new ConnectionLimitMiddleware<ConnectionContext>(c => innerDelegate(c), connectionLimit.Value, trace).OnConnectionAsync;
        }
    }

    //internal static class HttpConnectionBuilderExtensions
    //{
    //    public static IConnectionBuilder UseHttpServer<TContext>(this IConnectionBuilder builder, ServiceContext serviceContext, IHttpApplication<TContext> application, TerminalProtocols protocols)
    //    {
    //        var middleware = new HttpConnectionMiddleware<TContext>(serviceContext, application, protocols);
    //        return builder.Use(next =>
    //        {
    //            return middleware.OnConnectionAsync;
    //        });
    //    }
    //}

    ////我们需要的是终端连接，非http 改造它
    //internal class HttpConnectionMiddleware<TContext>
    //{
    //    private readonly ServiceContext _serviceContext;
    //    private readonly IHttpApplication<TContext> _application;
    //    private readonly TerminalProtocols _endpointDefaultProtocols;

    //    public HttpConnectionMiddleware(ServiceContext serviceContext, IHttpApplication<TContext> application, TerminalProtocols protocols)
    //    {
    //        _serviceContext = serviceContext;
    //        _application = application;
    //        _endpointDefaultProtocols = protocols;
    //    }

    //    public Task OnConnectionAsync(ConnectionContext connectionContext)
    //    {
    //        var memoryPoolFeature = connectionContext.Features.Get<IMemoryPoolFeature>();

    //        var httpConnectionContext = new HttpConnectionContext
    //        {
    //            ConnectionId = connectionContext.ConnectionId,
    //            ConnectionContext = connectionContext,
    //            Protocols = connectionContext.Features.Get<HttpProtocolsFeature>()?.HttpProtocols ?? _endpointDefaultProtocols,
    //            ServiceContext = _serviceContext,
    //            ConnectionFeatures = connectionContext.Features,
    //            MemoryPool = memoryPoolFeature?.MemoryPool ?? System.Buffers.MemoryPool<byte>.Shared,
    //            Transport = connectionContext.Transport,
    //            LocalEndPoint = connectionContext.LocalEndPoint as IPEndPoint,
    //            RemoteEndPoint = connectionContext.RemoteEndPoint as IPEndPoint
    //        };

    //        var connection = new HttpConnection(httpConnectionContext);

    //        return connection.ProcessRequestsAsync(_application);
    //    }
    //}
 
    internal class ConnectionLimitMiddleware<T> where T : BaseConnectionContext
    {
        private readonly Func<T, Task> _next;
        private readonly ResourceCounter _concurrentConnectionCounter;
        private readonly IKestrelTrace _trace;

        public ConnectionLimitMiddleware(Func<T, Task> next, long connectionLimit, IKestrelTrace trace)
            : this(next, ResourceCounter.Quota(connectionLimit), trace)
        {
        }

        // For Testing
        internal ConnectionLimitMiddleware(Func<T, Task> next, ResourceCounter concurrentConnectionCounter, IKestrelTrace trace)
        {
            _next = next;
            _concurrentConnectionCounter = concurrentConnectionCounter;
            _trace = trace;
        }

        public async Task OnConnectionAsync(T connection)
        {
            if (!_concurrentConnectionCounter.TryLockOne())
            {
                KestrelEventSource.Log.ConnectionRejected(connection.ConnectionId);
                _trace.ConnectionRejected(connection.ConnectionId);
                await connection.DisposeAsync();
                return;
            }

            var releasor = new ConnectionReleasor(_concurrentConnectionCounter);

            try
            {
                connection.Features.Set<IDecrementConcurrentConnectionCountFeature>(releasor);
                await _next(connection);
            }
            finally
            {
                releasor.ReleaseConnection();
            }
        }

        private class ConnectionReleasor : IDecrementConcurrentConnectionCountFeature
        {
            private readonly ResourceCounter _concurrentConnectionCounter;
            private bool _connectionReleased;

            public ConnectionReleasor(ResourceCounter normalConnectionCounter)
            {
                _concurrentConnectionCounter = normalConnectionCounter;
            }

            public void ReleaseConnection()
            {
                if (!_connectionReleased)
                {
                    _connectionReleased = true;
                    _concurrentConnectionCounter.ReleaseOne();
                }
            }
        }
    }
}
